package com.roadjava.rbac.security.config;

import com.roadjava.rbac.config.properties.AuthorizationProperties;
import com.roadjava.rbac.manager.CacheManager;
import com.roadjava.rbac.security.AuthenticationEntryPointImpl;
import com.roadjava.rbac.security.DecryptionFilter;
import com.roadjava.rbac.security.LoginAttemptService;
import com.roadjava.rbac.security.TokenAuthenticationFilter;
import com.roadjava.rbac.security.handler.AccessDeniedHandlerImpl;
import com.roadjava.rbac.security.handler.AuthenticationFailureHandlerImpl;
import com.roadjava.rbac.security.handler.AuthenticationSuccessHandlerImpl;
import com.roadjava.rbac.security.handler.LogoutHandlerImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private static final String SECRET_KEY = "AAAABBBBCCCCDDDD"; // 密钥

    @Resource
    private AuthenticationSuccessHandlerImpl authenticationSuccessHandler;
    @Resource
    private AuthenticationFailureHandlerImpl authenticationFailureHandler;
    @Resource
    private LogoutHandlerImpl logoutHandler;
    @Resource
    private AuthorizationProperties authorizationProperties;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    @Resource
    private CacheManager cacheManager;
    @Resource
    private AccessDeniedHandlerImpl accessDeniedHandler;
    @Resource
    private LoginAttemptService loginAttemptService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public TokenAuthenticationFilter tokenAuthenticationFilter() {
        return new TokenAuthenticationFilter(cacheManager);
    }

    @Bean
    public DecryptionFilter decryptionFilter() {
        return new DecryptionFilter(SECRET_KEY);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() // 禁用 CSRF 校验
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话
                .and()
                .formLogin()
                .passwordParameter("pwd") // 设置密码参数名称
                .loginProcessingUrl("/user/login") // 登录处理 URL
                .successHandler(authenticationSuccessHandler) // 登录成功处理器
                .failureHandler(authenticationFailureHandler) // 登录失败处理器
                .and()
                .addFilterBefore(decryptionFilter(), UsernamePasswordAuthenticationFilter.class) // 解密过滤器
                .addFilterBefore(tokenAuthenticationFilter(), LogoutFilter.class) // Token 认证过滤器
                .logout()
                .logoutUrl("/user/logout") // 注销 URL
                .addLogoutHandler(logoutHandler) // 注销处理器
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 允许所有 OPTIONS 请求
                .antMatchers(authorizationProperties.getWhiteList().toArray(new String[0])).permitAll() // 白名单路径
                .antMatchers(HttpMethod.OPTIONS, "/**", "/user/register", "/verify/captcha").permitAll() // 特殊路径允许访问
                .anyRequest().access("@rbacManager.hasPermission(request)") // 其他请求需要权限校验
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint) // 认证入口点
                .accessDeniedHandler(accessDeniedHandler); // 访问拒绝处理器
    }
}